package com.qs.commontools.utils.base;

import com.qs.commontools.common.exception.LengthErrorException;
import com.qs.commontools.common.service.StringService;
import com.qs.commontools.constants.RegularConstant;

import java.util.UUID;
import java.util.regex.Matcher;

/**
 * 字符串工具类
 * 参考：https://www.jianshu.com/p/413fb0370352
 * @author qiusuo
 * @since 2021-09-02
 */
public class StringUtils {
    // todo 字符串存在性判断
    /**
     * 判读是否为空。
     * <ul>
     *     <li>1.是否为 null</li>
     *     <li>2.是否为 ""</li>
     *     <li>3.是否为空字符串(引号中间有空格)  如： "     "。</li>
     *     <li>4.制表符、换行符、换页符和回车</li>
     *     <li>5.空白</li>
     * </ul>
     * <br>
     * <br> 调用示例：https://gitee.com/big-suo/common-tools/blob/develop/src/test/java/com/qs/commontools/string/StringTest.java
     *
     * @param str 判断字符串
     * @return 判断结果为无效字符，无效为<code>true</code>，有效为<code>false</code>
     */
    public static boolean isBlank(String str) {
        int strLen;
        if (str != null && (strLen = str.length()) != 0) {
            for(int i = 0; i < strLen; ++i) {
                if (!Character.isWhitespace(str.charAt(i))) {
                    return false;
                }
            }
            return true;
        } else {
            return true;
        }
    }

    /**
     * 判读是否为空。
     * <ul>
     *     <li>1.是否为 null</li>
     *     <li>2.str.length()==0</li>
     * </ul>
     * <br>
     * <br> 调用示例：https://gitee.com/big-suo/common-tools/blob/develop/src/test/java/com/qs/commontools/string/StringTest.java
     *
     * @param str 判断字符串
     * @return 判断结果为无效字符，无效为<code>true</code>，有效为<code>false</code>
     */
    public static boolean isEmpty(String str){
        return str == null || str.length() == 0;
    }

    // todo uuid
    /**
     * 获得简单的uuid，去除掉 "-"。
     *
     * @return uuid
     */
    public static String getSimpleUUID() {
        return UUID.randomUUID().toString().replaceAll("-","");
    }

    /**
     * 获得正常的uuid。
     *
     * @return uuid
     */
    public static String getUUID() {
        return UUID.randomUUID().toString();
    }


    /**
     * 下划线转驼峰。
     *
     * @param str 待转的字符串
     * @return 结果
     */
    public static String lineToLowerCamelCase(String str) {
        str = str.toLowerCase();
        Matcher matcher = RegularConstant.LINEPATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * 驼峰转下划线。
     *
     * @param str 待转的字符串
     * @return 结果
     */
    public static String lowerCamelCaseToLine(String str) {
        // 简单写法，效率较低
        // return str.replaceAll("[A-Z]", "_$0").toLowerCase()
        Matcher matcher = RegularConstant.HUMPPATTERN.matcher(str);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

//    /**
//     * 将对象的大写转换为下划线加小写，例如：userName-->user_name。
//     * <br>调用这个方法一定要引入jackson的依赖
//     *
//     * @param object
//     * @return
//     * @throws JsonProcessingException
//     */
//    public static String toUnderlineJSONString(Object object) throws JsonProcessingException {
//        ObjectMapper mapper = new ObjectMapper();
//        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
//        mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
//        String reqJson = mapper.writeValueAsString(object);
//        return reqJson;
//    }
//
//    /**
//     * 将下划线转换为驼峰的形式，例如：user_name-->userName
//     * @param json
//     * @param clazz
//     * @return
//     * @throws IOException
//     */
//    public static <T> T toSnakeObject(String json, Class<T> clazz) throws IOException {
//        ObjectMapper mapper = new ObjectMapper();
////      mapper的configure方法可以设置多种配置（例如：多字段 少字段的处理）
////　　　 mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
//        mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
//        T reqJson =  mapper.readValue(json, clazz);
//        return reqJson;
//    }

    // todo 字符串比较
    /**
     * 全等比较。
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return true 相等，false 不等
     */
    public static boolean equals(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equals(str2);
    }

    /**
     * 忽略前后空格比较。
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return true 相等，false 不等
     */
    public static boolean equalsByTrim(String str1, String str2) {
        return str1 == null ? str2 == null : str1.trim().equals(str2.trim());
    }

    /**
     * 忽略大小比较。
     *
     * @param str1 字符串1
     * @param str2 字符串2
     * @return true 相等，false 不等
     */
    public static boolean equalsIgnoreCase(String str1, String str2) {
        return str1 == null ? str2 == null : str1.equalsIgnoreCase(str2);
    }

    /**
     * 忽略前后空格和大小比较
     * @param str1 字符串1
     * @param str2 字符串2
     * @return true 相等，false 不等
     */
    public static boolean equalsByTrimIgnoreCase(String str1, String str2) {
        return equalsIgnoreCase(str1.trim(),str2.trim());
    }

    // todo  验证码
    /**
     * 获取验证码字符串,默认4位字符串。
     *
     * @return 验证码
     */
    public static String getVerification() {
        return getVerification(4);

    }

    /**
     * 获取验证码字符串，指定生成长度。
     *
     * @param num 指定的长度
     * @return 验证码
     */
    public static String getVerification(Integer num) {
        try {
            return StringService.verification(num);
        } catch (LengthErrorException e) {
            e.printStackTrace();
            return e.toString();
        }
    }

    /**
     * 获取验证码字符串，指定生成内容。
     *
     * @param chars 指定生成内容
     * @return 验证码
     */
    public static String getVerification(char[] chars) {
        return getVerification(4,chars);
    }

    /**
     * 获取验证码字符串，指定生成长度,指定生成内容。
     *
     * @param num 指定的长度
     * @param chars 指定生成内容
     * @return 验证码
     */
    public static String getVerification(Integer num,char[] chars) {
        try {
            return StringService.verification(num,chars);
        } catch (LengthErrorException e) {
            e.printStackTrace();
            return e.toString();
        }
    }

    /**
     * 从左侧截取指定长度字符串。
     *
     * @param str 待截取的字符串
     * @param len 截取长度
     * @return 截取后的字符串
     */
    public static String left(String str, int len) {
        if (str == null) {
            return null;
        } else if (len < 0) {
            return "";
        } else {
            return str.length() <= len ? str : str.substring(0, len);
        }
    }

    /**
     * 从右侧截取指定长度字符串。
     *
     * @param str 待截取的字符串
     * @param len 截取长度
     * @return 截取后的字符串
     */
    public static String right(String str, int len) {
        if (str == null) {
            return null;
        } else if (len < 0) {
            return "";
        } else {
            return str.length() <= len ? str : str.substring(str.length() - len);
        }
    }

    /**
     * 如果字符串a是以字符串b开始，则去掉这个开始，然后返回，否则返回原来的串。
     *
     * @param a 待判断截取的字符串
     * @param b 待判断的字符串
     * @return 返回的结果
     */
    public static String removeStart(String a, String b) {
        if (!isEmpty(a) && !isEmpty(b)) {
            return a.startsWith(b) ? a.substring(b.length()) : a;
        } else {
            return a;
        }
    }


    /**
     * 字符串根据指定索引位置补。
     *
     * @param repeat 拼接位置的索引
     * @param padChar 拼接的字符串
     * @return 补全的结果
     * @throws IndexOutOfBoundsException 超出索引长度
     */
    private static String padding(int repeat, char padChar) throws IndexOutOfBoundsException {
        if (repeat < 0) {
            throw new IndexOutOfBoundsException("Cannot pad a negative amount: " + repeat);
        } else {
            char[] buf = new char[repeat];

            for(int i = 0; i < buf.length; ++i) {
                buf[i] = padChar;
            }

            return new String(buf);
        }
    }

    /**
     * 字符串左侧补全。
     *
     * @param str 待补全的字符串
     * @param size 补全后的期望字符串长度
     * @return 结果
     */
    public static String leftPad(String str, int size) {
        return leftPad(str, size, ' ');
    }

    /**
     * 字符串左侧补全。
     *
     * @param str 待补全的字符串
     * @param size 补全后的期望字符串长度
     * @param padChar 补char类型
     * @return 结果
     */
    public static String leftPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        } else {
            int pads = size - str.length();
            if (pads <= 0) {
                return str;
            } else {
                return pads > 8192 ? leftPad(str, size, String.valueOf(padChar)) : padding(pads, padChar).concat(str);
            }
        }
    }

    /**
     * 字符串左侧补全。
     *
     * @param str 待补全的字符串
     * @param size 补全后的期望字符串长度
     * @param padStr 补的字符串
     * @return 结果
     */
    public static String leftPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        } else {
            if (isEmpty(padStr)) {
                padStr = " ";
            }

            int padLen = padStr.length();
            int strLen = str.length();
            int pads = size - strLen;
            if (pads <= 0) {
                return str;
            } else if (padLen == 1 && pads <= 8192) {
                return leftPad(str, size, padStr.charAt(0));
            } else if (pads == padLen) {
                return padStr.concat(str);
            } else if (pads < padLen) {
                return padStr.substring(0, pads).concat(str);
            } else {
                char[] padding = new char[pads];
                char[] padChars = padStr.toCharArray();

                for(int i = 0; i < pads; ++i) {
                    padding[i] = padChars[i % padLen];
                }

                return (new String(padding)).concat(str);
            }
        }
    }

    /**
     * 字符串右侧补全。
     *
     * @param str 待补全的字符串
     * @param size 期望补全后的长度
     * @return 返回补全后的结果
     */
    public static String rightPad(String str, int size) {
        return rightPad(str, size, ' ');
    }

    public static String rightPad(String str, int size, char padChar) {
        if (str == null) {
            return null;
        } else {
            int pads = size - str.length();
            if (pads <= 0) {
                return str;
            } else {
                return pads > 8192 ? rightPad(str, size, String.valueOf(padChar)) : str.concat(padding(pads, padChar));
            }
        }
    }

    /**
     * 字符串右侧补全。
     *
     * @param str 待补全的字符串
     * @param size 期望补全后的长度
     * @param padStr 补的字符串
     * @return 返回补全后的结果
     */
    public static String rightPad(String str, int size, String padStr) {
        if (str == null) {
            return null;
        } else {
            if (isEmpty(padStr)) {
                padStr = " ";
            }

            int padLen = padStr.length();
            int strLen = str.length();
            int pads = size - strLen;
            if (pads <= 0) {
                return str;
            } else if (padLen == 1 && pads <= 8192) {
                return rightPad(str, size, padStr.charAt(0));
            } else if (pads == padLen) {
                return str.concat(padStr);
            } else if (pads < padLen) {
                return str.concat(padStr.substring(0, pads));
            } else {
                char[] padding = new char[pads];
                char[] padChars = padStr.toCharArray();

                for(int i = 0; i < pads; ++i) {
                    padding[i] = padChars[i % padLen];
                }

                return str.concat(new String(padding));
            }
        }
    }

    /**
     * 字符串长度。
     *
     * @param str 字符串
     * @return 长度
     */
    public static int length(String str) {
        return str == null ? 0 : str.length();
    }


}
